package pl.skubicha.controller;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import org.apache.commons.lang.StringUtils;
import org.jboss.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import pl.skubicha.controller.property.editors.GroupsPropertyEditor;
import pl.skubicha.controller.property.editors.UsersPropertyEditor;
import pl.skubicha.domain.Groups;
import pl.skubicha.domain.Subjects;
import pl.skubicha.domain.Users;
import pl.skubicha.exceptions.EntityConstraintException;
import pl.skubicha.exceptions.SubjectNotFoundException;
import pl.skubicha.exceptions.UserNotFoundException;
import pl.skubicha.response.SearchDTO;
import pl.skubicha.response.SubjectDTO;
import pl.skubicha.service.GroupsService;
import pl.skubicha.service.SubjectService;
import pl.skubicha.service.UserService;
import pl.skubicha.utils.GroupMapper;
import pl.skubicha.utils.SubjectMapper;
import pl.skubicha.utils.Utils;

@Controller
@RequestMapping( { "/subjects" } )
public class SubjectsController {

	private static Logger logger = Logger.getLogger( SubjectsController.class );

	private static final String SUBJECT_NAME = "subjectName";
	private static final String TEACHER_FULL_NAME = "fullName";

	private static final int SUBJECTS_PER_PAGE = 10;
	private static final int DEFAULT_PAGE = 1;

	@Autowired
	private SubjectService service;

	@Autowired
	private UserService userService;

	@Autowired
	private GroupsService groupService;

	@ModelAttribute( "allTeachers" )
	public List< Users > getAllTeachers() {
		return userService.findAllTeachers();
	}

	@ModelAttribute( "allGroups" )
	public List< Groups > getAllGroups() {
		return groupService.findAll();
	}

	@InitBinder
	protected void initBinder( HttpServletRequest request, ServletRequestDataBinder binder ) {
		binder.registerCustomEditor( Users.class, new UsersPropertyEditor( this.userService ) ); 
		binder.registerCustomEditor( Groups.class, new GroupsPropertyEditor( this.groupService ) );
	}
	
	@PreAuthorize("hasAnyRole('Student')")
	@RequestMapping( value = { "/mysubjects" }, method = RequestMethod.GET )
	public String getMySubjects( final ModelMap model ){
		try {
			Users user = userService.findByLogin( SecurityContextHolder.getContext().getAuthentication().getName() );			
			model.addAttribute( "group", GroupMapper.map( user.getGroups() ));
		} catch ( UserNotFoundException e ) {			
		}
		return "subjects/mySubjects";
	}

	@RequestMapping( { "", "/" } )
	@PreAuthorize("hasAnyRole('Administrator','Wykładowca')")
	public String defaultPage( final ModelMap model, @RequestParam( required = false ) final String order, @RequestParam( required = false ) final String column, @RequestParam( required = false ) final String searchString ) {
		getPageWithNumber( DEFAULT_PAGE, model, order, column, searchString );
		return "subjects/list";
	}

	@RequestMapping( "/{pageNumber}" )
	@PreAuthorize("hasAnyRole('Administrator','Wykładowca')")
	public String getPageWithNumber( @PathVariable final Integer pageNumber, final ModelMap model, @RequestParam( required = false ) final String order, @RequestParam( required = false ) final String column, @RequestParam( required = false ) final String searchString ) {
		getPage( pageNumber, model, order, column, searchString );
		return "subjects/list";
	}

	private void getPage( final Integer pageNumber, final ModelMap model, final String order, final String column, final String searchString ) {
		final Page< Subjects > subjectsPage = getSubjectsPerPage( searchString, new PageRequest( pageNumber - 1, SUBJECTS_PER_PAGE, Utils.getSortingDirection( order ), getSortedColumns( column ) ) );
		final int currentPage = subjectsPage.getNumber() + 1;
		final int beginPage = Math.max( 1, currentPage - 5 );
		final int endPage = Math.min( beginPage + 10, ( subjectsPage.getTotalPages() == 0 ? 1 : subjectsPage.getTotalPages() ) );
		final int totalPages = ( subjectsPage.getTotalPages() == 0 ? 1 : subjectsPage.getTotalPages() );

		model.addAttribute( "subjectsPage", subjectsPage );
		model.addAttribute( "totalRecords", subjectsPage.getTotalElements() );
		model.addAttribute( "beginIndex", beginPage );
		model.addAttribute( "endIndex", endPage );
		model.addAttribute( "currentIndex", currentPage );
		model.addAttribute( "totalPages", totalPages );
		model.addAttribute( "subjects", SubjectMapper.map( subjectsPage ) );
		model.addAttribute( "order", "ASC".equals( order ) ? "DESC": "ASC");
		model.addAttribute( "column", getSortedColumn( column ) );
		model.addAttribute( "searchHelper", new SearchDTO( searchString ) );
	}

	@RequestMapping( value = { "/create" }, method = RequestMethod.GET )
	@PreAuthorize("hasAnyRole('Administrator','Wykładowca')")
	public String create( final ModelMap model ) {
		model.addAttribute( "subjectDTO", new SubjectDTO() );
		model.addAttribute( "type", "create" );
		return "subjects/edit";
	}

	@RequestMapping( value = { "/create" }, method = RequestMethod.POST )
	@PreAuthorize("hasAnyRole('Administrator','Wykładowca')")
	public String createNewSubject( @Valid final SubjectDTO subjectDTO, BindingResult bindingResult,final ModelMap model ) {
		
		if ( bindingResult.hasErrors() ) {
			model.addAttribute( "type", "create" );
			return "subjects/edit";
		}
		
		try {
			service.create( subjectDTO );
			model.addAttribute( "status", "subjects.create.success" );
		} catch ( final Exception ex ) {
			model.addAttribute( "status", "subjects.create.failure" );
			logger.debug( ex );
		}

		getPage( DEFAULT_PAGE, model, Utils.getSortingDirection( StringUtils.EMPTY ).toString(), StringUtils.EMPTY, StringUtils.EMPTY );

		return "subjects/list";
	}

	@RequestMapping( value = { "/edit/{id}" }, method = RequestMethod.GET )
	@PreAuthorize("hasAnyRole('Administrator','Wykładowca')")
	public String edit( @PathVariable( value = "id" ) final Integer id, final ModelMap model ) {

		try {
			Subjects subject = service.findById( id );
			model.addAttribute( "subjectDTO", SubjectMapper.map( subject ) );
			model.addAttribute( "type", "edit" );
		} catch ( final SubjectNotFoundException ex ) {
			logger.debug( ex );
			model.addAttribute( "status", "subjects.not.found" );
			getPage( DEFAULT_PAGE, model, Utils.getSortingDirection( StringUtils.EMPTY ).toString(), StringUtils.EMPTY, StringUtils.EMPTY );
			return "subjects/list";
		}

		return "subjects/edit";
	}

	@RequestMapping( value = { "/edit" }, method = RequestMethod.POST )
	@PreAuthorize("hasAnyRole('Administrator','Wykładowca')")
	public String update( @Valid final SubjectDTO subjectDTO, BindingResult bindingResult, final ModelMap model ) {
		
		if ( bindingResult.hasErrors() ) {
			model.addAttribute( "type", "edit" );
			return "subjects/edit";
		}
		
		try {
			service.update( subjectDTO );
			model.addAttribute( "status", "subjects.update.success" );
		} catch ( final SubjectNotFoundException ex ) {
			model.addAttribute( "status", "subjects.not.found" );
			logger.debug( ex );
		}
		getPage( DEFAULT_PAGE, model, Utils.getSortingDirection( StringUtils.EMPTY ).toString(), StringUtils.EMPTY, StringUtils.EMPTY );

		return "subjects/list";
	}

	@RequestMapping( value = "/delete/{id}" )
	@PreAuthorize("hasAnyRole('Administrator','Wykładowca')")
	public String delete( @PathVariable( value = "id" ) final Integer id, final ModelMap model ) {
		try {
			service.delete( id );
			model.addAttribute( "status", "subjects.delete.success" );
		} catch ( final SubjectNotFoundException ex ) {
			model.addAttribute( "status", "subjects.not.found" );
			logger.debug( ex );
		} catch ( EntityConstraintException ex ) {
			model.addAttribute( "status", "subjects.constraint.exception" );
			logger.debug( ex );
		}

		getPage( DEFAULT_PAGE, model, Utils.getSortingDirection( StringUtils.EMPTY ).toString(), SUBJECT_NAME, StringUtils.EMPTY );
		return "subjects/list";
	}

	private Page< Subjects > getSubjectsPerPage( final String searchString, final PageRequest pageRequest ) {
		Page< Subjects > SubjectsPage = null;

		if ( StringUtils.isEmpty( searchString ) ) {
			SubjectsPage = service.findAll( pageRequest );
		} else {
			SubjectsPage = service.findAllMatching( searchString, pageRequest );
		}
		return SubjectsPage;
	}

	private String[] getSortedColumns( String column ) {
		if ( TEACHER_FULL_NAME.equals( column ) ) {
			return new String[] { "users.firstName", "users.lastName" };
		}

		return new String[] { getSortedColumn( column ) };
	}

	private String getSortedColumn( String column ) {
		if ( StringUtils.isEmpty( column ) ) {
			column = SUBJECT_NAME;
		} else if ( !column.equals( SUBJECT_NAME ) && !column.equals( TEACHER_FULL_NAME ) ) {
			column = SUBJECT_NAME;
		} else if ( column.equals( TEACHER_FULL_NAME ) ) {
			column = TEACHER_FULL_NAME;
		}
		return column;
	}
}